package paim.wingchun.view.componentes.table;

import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.EventListenerList;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import paim.wingchun.app.Reflections;
import paim.wingchun.controller.WC_Event;
import paim.wingchun.model.pojos.Pojo;
import paim.wingchun.view.FireListener;
import paim.wingchun.view.Fireable;
import paim.wingchun.view.componentes.table.WC_TableModel.TipoColuna;

public class WC_JTable<P extends Pojo> extends JTable implements Fireable<CarregarListener> {

    private static final long serialVersionUID = 1L;

    /* = ListSelectionModel.SINGLE_SELECTION */
    final int selectionMode;

    protected enum Eventos {
        CARREGAR, SELECIONAR
    }

    public WC_JTable(Class<P> pClass) {
        this(pClass, ListSelectionModel.SINGLE_SELECTION, true, false, false);
    }

    public WC_JTable(Class<P> pClass, int selectionMode, boolean hasSequencial, boolean hasSeletor,
            boolean hasConstrutor) {
        /* modelo de dados */
        super(new WC_TableModel(pClass, selectionMode, hasSequencial, hasSeletor, hasConstrutor));
        this.selectionMode = selectionMode;
        initialize();
    }

    public WC_JTable() {
        this(ListSelectionModel.SINGLE_SELECTION, true, false, false);
    }

    public WC_JTable(int selectionMode, boolean hasSequencial, boolean hasSeletor, boolean hasConstrutor) {
        /* modelo de dados */
        super();
        WC_TableModel model = new WC_TableModel(Reflections.getClassParameter(getClass(), Pojo.class), selectionMode,
                        hasSequencial, hasSeletor, hasConstrutor);
        this.setModel(model);
        this.selectionMode = selectionMode;
        initialize();
    }

    protected void initialize() {
        /* setAutoCreateRowSorter(true); */
        setRowSorter(new WC_TableRowSorter((WC_TableModel) getModel()));

        // FIXME
        getColumnModel().getColumn(2).setCellRenderer(new WC_TableCellRenderer());

        getSelectionModel().setSelectionMode(selectionMode);
        // if ( ListSelectionModel.SINGLE_SELECTION == selectionMode ) {
        //
        // int selectorColumn = ((WCTableModel) getModel()).getSelectorColumn();
        // getColumnModel().getColumn(selectorColumn).setCellRenderer(new RadioButtonRenderer());
        // getColumnModel().getColumn(selectorColumn).setCellEditor(new RadioButtonEditor(new JCheckBox()));
        // }

        ajustaLarguraColunas();

        /* FIXME se tem a coluna seletor entao eh porque pode selecionar mais de um */
        // if ( mapColunas.containsKey(TipoColuna.SELETOR) )
        // getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

        setTableHeader(newJTableHeader());

        setColumnSelectionAllowed(false);
        setNavegarColunasByTeclado(false);

        addMouseListener(new FireListener(this, Eventos.CARREGAR));
        getSelectionModel().addListSelectionListener(new FireListener(this, Eventos.SELECIONAR));
        // getModel().addTableModelListener(new TableModelListener() {
        //
        // @Override
        // public void tableChanged(TableModelEvent e) {
        // if ( getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION ) {
        // ((WCTableModel) getModel()).deselectOthers(e.getLastRow());
        // }
        //
        // }
        // });
    }

    @Override
    public EventListenerList getListenersList() {
        return this.listenerList;
    }

    @Override
    public void fire(WC_Event e) {
        //CarregarListener[] listeners = listenerList.getListeners(CarregarListener.class);
        for ( CarregarListener listener : fireListeners() ) {
            switch ( (Eventos) e.evento() ) {
                case CARREGAR:
                    listener.carregar();
                break;
                case SELECIONAR:
                    listener.selecionar();
                break;
                default:
            }
        }
    }

    private void ajustaLarguraColunas() {
        TableColumnModel columnModel = getColumnModel();

        /* justar largura de colunas */
        if ( ((WC_TableModel) getModel()).haveSequencial() ) {
            TableColumn column = columnModel.getColumn(((WC_TableModel) getModel()).getSequencialColumn());
            column.setMinWidth(24);
            column.setMaxWidth(30);
            column.setPreferredWidth(24);
        }

        /* justar largura de colunas */
        if ( ((WC_TableModel) getModel()).haveSelector() ) {
            TableColumn column = columnModel.getColumn(((WC_TableModel) getModel()).getSelectorColumn());
            column.setMinWidth(20);
            column.setMaxWidth(20);
            column.setPreferredWidth(20);
        }

        /* justar largura de colunas */
        if ( ((WC_TableModel) getModel()).haveConstructor() ) {
            TableColumn column = columnModel.getColumn(((WC_TableModel) getModel()).getConstructorColumn());
            column.setMinWidth(10);
            column.setMaxWidth(10);
            column.setPreferredWidth(10);
        }

    }

    private JTableHeader newJTableHeader() {
        Map<TipoColuna, List<Integer>> mapColunas = ((WC_TableModel) getModel()).mapColunas;

        JTableHeader header = new JTableHeader(getColumnModel());
        ColumnHeaderToolTipsListener tips = new ColumnHeaderToolTipsListener();

        /* Seta o tooltip para cada coluna do tipo Seletor */
        if ( mapColunas.containsKey(TipoColuna.SELECTOR) ) {
            int c = mapColunas.get(TipoColuna.SELECTOR).get(0);
            TableColumn coluna = this.getColumnModel().getColumn(c);
            tips.setToolTip(coluna, "Selecione... ");
        }

        /* Seta o tooltip para cada coluna do tipo field */
        if ( mapColunas.containsKey(TipoColuna.FIELD) )
            for ( Integer c : mapColunas.get(TipoColuna.FIELD) ) {
                TableColumn coluna = this.getColumnModel().getColumn(c);
                tips.setToolTip(coluna, "Click para ordenar por " + this.getModel().getColumnName(c));
            }

        // TODO falta construtor e ou sequencial

        header.addMouseMotionListener(tips);
        return header;
    }

    public void setNavegarColunasByTeclado(boolean navegar) {
        String sKeyAction = "tabOut";
        if ( !navegar ) {
            /* nao deixa ficar navegando entre colunas */
            this.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), sKeyAction);
            this.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), sKeyAction);
            this.getActionMap().put(sKeyAction, new acaoTabOut());
        }
        else {
            // TODO
        }
    }

    /** nao deixa ficar navegando entre colunas quando navegar com tecla tab */
    private class acaoTabOut extends AbstractAction {

        private static final long serialVersionUID = 1L;

        @Override
        public void actionPerformed(ActionEvent e) {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
        }

    }

    public void setObjetos(List<?> objetos) {
        ((WC_TableModel) getModel()).setObjetos(objetos);
        getRowSorter().allRowsChanged();
    }

    public <T> List<T> getObjetos() {
        @SuppressWarnings("unchecked")
        List<T> lista = (List<T>) ((WC_TableModel) getModel()).getObjetos();
        return lista;
    }

    public void clear() {
        ((WC_TableModel) getModel()).clear();
        getRowSorter().allRowsChanged();
    }

    // @Deprecated
    // public void setSelecionados(List<Object> objetosSelecionados) {
    // ((WCTableModel) getModel()).setSelecionados(objetosSelecionados);
    // }
    //
    // @Deprecated
    // public void setSelecionado(Object objetoSelecionado) {
    // if ( objetoSelecionado == null )
    // return;
    //
    // List<Object> lista = new ArrayList<Object>(1);
    // lista.add(objetoSelecionado);
    // setSelecionados(lista);
    // }

    // @Deprecated
    // @SuppressWarnings("unchecked")
    // public <T> List<T> getSelecionados() {
    // return (List<T>) ((WCTableModel) getModel()).getSelecionados();
    // }

    public void setSelecao(Object selecao) {
        if ( selecao == null )
            return;
        /* manda essa lista para modelo */
        if ( List.class.isAssignableFrom(selecao.getClass()) ) {
            ((WC_TableModel) getModel()).setSelecionados((List<Object>) selecao);
        }
        /* cria uma lista e manda para o modelo */
        else {
            List<Object> lista = new ArrayList<Object>(1);
            lista.add(selecao);
            ((WC_TableModel) getModel()).setSelecionados(lista);
        }
    }

    public Object getSelecao() {
        switch ( getSelectionModel().getSelectionMode() ) {
            case ListSelectionModel.SINGLE_SELECTION:
                List<?> lista = ((WC_TableModel) getModel()).getSelecionados();
                if ( lista.isEmpty() )
                    return null;
                return lista.get(0);

            case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
                return ((WC_TableModel) getModel()).getSelecionados();

            default:
                return null;
        }
    }

    @Override
    public int getSelectedRow() {
        /* obtem o index da tabela reordenada pelo usuario */
        int index = super.getSelectedRow();
        if ( index < 0 )
            return index;

        /* obtem index original correspondente a colecao */
        int indexOriginal = ((WC_TableRowSorter) getRowSorter()).convertRowIndexToModel(index);
        return indexOriginal;

    }

    public P getSelectedObject() {
        /* obtem index original correspondente a colecao */
        int index = getSelectedRow();

        if ( index < 0 )
            return null;

        P objeto = ((WC_TableModel) getModel()).get(index);
        return objeto;

    }

    // private class RowListener implements ListSelectionListener {
    // public void valueChanged(ListSelectionEvent event) {
    // if (event.getValueIsAdjusting()) {
    // return;
    // }
    // output.append("ROW SELECTION EVENT. ");
    // outputSelection();
    // }
    // }
    //
    // private class ColumnListener implements ListSelectionListener {
    // public void valueChanged(ListSelectionEvent event) {
    // if (event.getValueIsAdjusting()) {
    // return;
    // }
    // output.append("COLUMN SELECTION EVENT. ");
    // outputSelection();
    // }
    // }

}
